package org.fhm.zdte.protocol.zrudp.service;

import org.fhm.substrate.annotation.Component;
import org.fhm.substrate.annotation.Setup;
import org.fhm.zdte.common.ability.ITirConsumer;
import org.fhm.zdte.common.protocol.AbstractMessageStandard;
import org.fhm.zdte.common.util.CommonUtil;
import org.fhm.zdte.protocol.zrudp.constant.DatagramHead;
import org.fhm.zdte.protocol.zrudp.standard.IDataConvertor;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Supplier;

/**
 * Common data convertor that is converting between Packet-Message and Packet-Data.
 *
 * @author 谭波
 * @since 2024/2/1
 */
@Component
public class CommonDataConvertor implements IDataConvertor<DatagramHead> {

    /**
     * The {@link ThreadLocal} of a pipeline.
     */
    private final ThreadLocal<StreamPipeline<DatagramHead>> sp = new ThreadLocal<>();

    /**
     * The {@link ThreadLocal} of convertor.
     */
    private final ThreadLocal<StreamConvertor<DatagramHead>> sc = new ThreadLocal<>();

    @Setup
    private CommonUtil commonUtil;

    public StreamPipeline<DatagramHead> ofPipeline(Supplier<DatagramHead[]> supplier) {
        return commonUtil.obtainObjOfThreadLocal(sp, () -> new StreamPipeline<>(supplier.get()));
    }

    public StreamConvertor<DatagramHead> ofConvertor(Supplier<DatagramHead[]> supplier) {
        return commonUtil.obtainObjOfThreadLocal(sc, () -> new StreamConvertor<>(supplier.get()));
    }

    public static class StreamPipeline<R> implements IPipeline<R> {

        private final R[] discriminantData;

        private final List<Byte> stream = new ArrayList<>();

        public StreamPipeline(R[] rStream) {
            this.discriminantData = rStream;
        }

        @Override
        public IPipeline<R> joinFor(BiConsumer<R, List<Byte>> function) {
            stream.clear();
            for (R r : discriminantData) {
                function.accept(r, stream);
            }
            return this;
        }

        @Override
        public byte[] generation() {
            int len;
            byte[] bytes = new byte[(len = stream.size())];
            for (int i = 0; i < len; i++) {
                bytes[i] = stream.get(i);
            }
            return bytes;
        }
    }

    public static class StreamConvertor<R> implements IConvertor<R> {

        private final R[] discriminantData;

        private AbstractMessageStandard message;

        private List<List<Byte>> headList;

        private BiFunction<R, Byte, Boolean> matchFunction;


        public StreamConvertor(R[] discriminantData) {
            this.discriminantData = discriminantData;
        }

        @Override
        public IConvertor<R> prepare(Supplier<List<List<Byte>>> supplier, AbstractMessageStandard message) {
            this.message = message;
            this.headList = supplier.get();
            return this;
        }

        public IConvertor<R> setMatchFunction(BiFunction<R, Byte, Boolean> matchFunction) {
            this.matchFunction = matchFunction;
            return this;
        }

        @Override
        public void makeFor(ITirConsumer<R, AbstractMessageStandard, List<Byte>> function) {
            headList.forEach(
                    hv -> {
                        Byte head = hv.remove(0);
                        for (R r : discriminantData) {
                            if (matchFunction.apply(r, head))
                                function.accept(r, message, hv);
                        }
                    }
            );
        }
    }

}
